/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

var zrUtil = require("static/plugins/js/zrender/lib/core/util");

var RoamController = require("../../component/helper/RoamController");

var throttleUtil = require("../../util/throttle");

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
// Only create one roam controller for each coordinate system.
// one roam controller might be refered by two inside data zoom
// components (for example, one for x and one for y). When user
// pan or zoom, only dispatch one action for those data zoom
// components.
var ATTR = "\0_ec_dataZoom_roams";
/**
 * @public
 * @param {module:echarts/ExtensionAPI} api
 * @param {Object} dataZoomInfo
 * @param {string} dataZoomInfo.coordId
 * @param {Function} dataZoomInfo.containsPoint
 * @param {Array.<string>} dataZoomInfo.allCoordIds
 * @param {string} dataZoomInfo.dataZoomId
 * @param {Object} dataZoomInfo.getRange
 * @param {Function} dataZoomInfo.getRange.pan
 * @param {Function} dataZoomInfo.getRange.zoom
 * @param {Function} dataZoomInfo.getRange.scrollMove
 * @param {boolean} dataZoomInfo.dataZoomModel
 */

function register(api, dataZoomInfo) {
    var store = giveStore(api);
    var theDataZoomId = dataZoomInfo.dataZoomId;
    var theCoordId = dataZoomInfo.coordId; // Do clean when a dataZoom changes its target coordnate system.
    // Avoid memory leak, dispose all not-used-registered.

    zrUtil.each(store, function (record, coordId) {
        var dataZoomInfos = record.dataZoomInfos;

        if (
            dataZoomInfos[theDataZoomId] &&
            zrUtil.indexOf(dataZoomInfo.allCoordIds, theCoordId) < 0
        ) {
            delete dataZoomInfos[theDataZoomId];
            record.count--;
        }
    });
    cleanStore(store);
    var record = store[theCoordId]; // Create if needed.

    if (!record) {
        record = store[theCoordId] = {
            coordId: theCoordId,
            dataZoomInfos: {},
            count: 0,
        };
        record.controller = createController(api, record);
        record.dispatchAction = zrUtil.curry(dispatchAction, api);
    } // Update reference of dataZoom.

    !record.dataZoomInfos[theDataZoomId] && record.count++;
    record.dataZoomInfos[theDataZoomId] = dataZoomInfo;
    var controllerParams = mergeControllerParams(record.dataZoomInfos);
    record.controller.enable(
        controllerParams.controlType,
        controllerParams.opt
    ); // Consider resize, area should be always updated.

    record.controller.setPointerChecker(dataZoomInfo.containsPoint); // Update throttle.

    throttleUtil.createOrUpdate(
        record,
        "dispatchAction",
        dataZoomInfo.dataZoomModel.get("throttle", true),
        "fixRate"
    );
}
/**
 * @public
 * @param {module:echarts/ExtensionAPI} api
 * @param {string} dataZoomId
 */

function unregister(api, dataZoomId) {
    var store = giveStore(api);
    zrUtil.each(store, function (record) {
        record.controller.dispose();
        var dataZoomInfos = record.dataZoomInfos;

        if (dataZoomInfos[dataZoomId]) {
            delete dataZoomInfos[dataZoomId];
            record.count--;
        }
    });
    cleanStore(store);
}
/**
 * @public
 */

function generateCoordId(coordModel) {
    return coordModel.type + "\0_" + coordModel.id;
}
/**
 * Key: coordId, value: {dataZoomInfos: [], count, controller}
 * @type {Array.<Object>}
 */

function giveStore(api) {
    // Mount store on zrender instance, so that we do not
    // need to worry about dispose.
    var zr = api.getZr();
    return zr[ATTR] || (zr[ATTR] = {});
}

function createController(api, newRecord) {
    var controller = new RoamController(api.getZr());
    zrUtil.each(["pan", "zoom", "scrollMove"], function (eventName) {
        controller.on(eventName, function (event) {
            var batch = [];
            zrUtil.each(newRecord.dataZoomInfos, function (info) {
                // Check whether the behaviors (zoomOnMouseWheel, moveOnMouseMove,
                // moveOnMouseWheel, ...) enabled.
                if (!event.isAvailableBehavior(info.dataZoomModel.option)) {
                    return;
                }

                var method = (info.getRange || {})[eventName];
                var range = method && method(newRecord.controller, event);
                !info.dataZoomModel.get("disabled", true) &&
                    range &&
                    batch.push({
                        dataZoomId: info.dataZoomId,
                        start: range[0],
                        end: range[1],
                    });
            });
            batch.length && newRecord.dispatchAction(batch);
        });
    });
    return controller;
}

function cleanStore(store) {
    zrUtil.each(store, function (record, coordId) {
        if (!record.count) {
            record.controller.dispose();
            delete store[coordId];
        }
    });
}
/**
 * This action will be throttled.
 */

function dispatchAction(api, batch) {
    api.dispatchAction({
        type: "dataZoom",
        batch: batch,
    });
}
/**
 * Merge roamController settings when multiple dataZooms share one roamController.
 */

function mergeControllerParams(dataZoomInfos) {
    var controlType; // DO NOT use reserved word (true, false, undefined) as key literally. Even if encapsulated
    // as string, it is probably revert to reserved word by compress tool. See #7411.

    var prefix = "type_";
    var typePriority = {
        type_true: 2,
        type_move: 1,
        type_false: 0,
        type_undefined: -1,
    };
    var preventDefaultMouseMove = true;
    zrUtil.each(dataZoomInfos, function (dataZoomInfo) {
        var dataZoomModel = dataZoomInfo.dataZoomModel;
        var oneType = dataZoomModel.get("disabled", true)
            ? false
            : dataZoomModel.get("zoomLock", true)
            ? "move"
            : true;

        if (
            typePriority[prefix + oneType] > typePriority[prefix + controlType]
        ) {
            controlType = oneType;
        } // Prevent default move event by default. If one false, do not prevent. Otherwise
        // users may be confused why it does not work when multiple insideZooms exist.

        preventDefaultMouseMove &= dataZoomModel.get(
            "preventDefaultMouseMove",
            true
        );
    });
    return {
        controlType: controlType,
        opt: {
            // RoamController will enable all of these functionalities,
            // and the final behavior is determined by its event listener
            // provided by each inside zoom.
            zoomOnMouseWheel: true,
            moveOnMouseMove: true,
            moveOnMouseWheel: true,
            preventDefaultMouseMove: !!preventDefaultMouseMove,
        },
    };
}

exports.register = register;
exports.unregister = unregister;
exports.generateCoordId = generateCoordId;
